@_silgen_name("swift_getTypeByMangledNameInContext")
func dw_swift_getTypeByMangledNameInContext(typeName: UnsafeRawPointer, len: Int, context: UnsafeRawPointer?, generic: UnsafeRawPointer?) -> Any.Type?

struct DWStructMetaData{
    var kind: Int
    var typeDesc: UnsafeMutablePointer<DWStructDesc>
}

struct DWStructDesc{
    var flags : Int32
    var parent : DWRelativeDirtPointer<UnsafeRawPointer>
    var name : DWRelativeDirtPointer<CChar>
    var accessFunctionPointer : DWRelativeDirtPointer<UnsafeRawPointer>
    var fieldDescriptor : DWRelativeDirtPointer<DWFieldDesc>
    var numFields : UInt32
    var fieldOffset: UInt32
    func getFieldOffsets(_ metadata: UnsafeRawPointer) -> UnsafePointer<Int> {
        return UnsafeRawPointer(metadata.assumingMemoryBound(to: Int.self).advanced(by: numericCast(self.fieldOffset))).assumingMemoryBound(to: Int.self)
    }
    var genericOffset: Int {2}
}

struct DWClassMetaData {
    var kind: Int
    var superClass: Any.Type?
    var cacheData: (Int, Int)
    var data: Int
    var classFlags: Int32
    var instanceAddressPoint: UInt32
    var instanceSize: UInt32
    var instanceAlignmentMask: UInt16
    var reserved: UInt16
    var classSize: UInt32
    var classAddressPoint: UInt32
    var typeDesc: UnsafeMutablePointer<DWClassDesc>
    var iVarDestroyer: UnsafeRawPointer
}

struct DWClassDesc{
    var flags: UInt32
    var parent: DWRelativeDirtPointer<UnsafeRawPointer>
    var name: DWRelativeDirtPointer<CChar>
    var accessFunctionPointer: DWRelativeDirtPointer<UnsafeRawPointer>
    var fieldDescriptor: DWRelativeDirtPointer<DWFieldDesc>
    var superClassType: DWRelativeDirtPointer<CChar>
    var metadataNegativeSizeInWords: UInt32
    var metadataPositiveSizeInWords: UInt32
    var numImmediateMembers: UInt32
    var numFields: UInt32
    var fieldOffset: UInt32
    func getFieldOffsets(_ metadata: UnsafeRawPointer) -> UnsafePointer<Int> {
        return metadata.assumingMemoryBound(to: Int.self).advanced(by: numericCast(self.fieldOffset))
    }
    var genericOffset: Int { return 2 }
}

struct DWProtocolMetaData {
    let type: Any.Type
    let witness: Int
}

struct DWEnumMetaData {
    var kind: Int
    var typeDesc: UnsafeMutablePointer<DWEnumDesc>
}

struct DWEnumDesc {
    var flags: UInt32
    var parent: DWRelativeDirtPointer<UnsafeRawPointer>
    var name: DWRelativeDirtPointer<CChar>
    var accessFunctionPointer: DWRelativeDirtPointer<UnsafeRawPointer>
    var fieldDescriptor: DWRelativeDirtPointer<DWFieldDesc>
    var numPayloadCasesAndPayloadSizeOffset: UInt32
    var numEmptyCases: UInt32
}

struct DWTupleMetaData {
    let kind: UnsafeRawPointer
    let numElements: Int
    let labels: UnsafeMutablePointer<CChar>
    var elements: DWFiledRecordBuffer<DWTupleElement>
}

struct DWTupleElement {
    let type: Any.Type
    let offset: Int
}

struct DWRelativeDirtPointer<Pointee>{
    var offset: Int32
    mutating func relativeOffset() -> UnsafeMutablePointer<Pointee>{
        let offset = self.offset
        return withUnsafePointer(to: &self) { p in
            return UnsafeMutablePointer(mutating: UnsafeRawPointer(p).advanced(by: numericCast(offset)).assumingMemoryBound(to: Pointee.self))
        }
    }
}

struct DWFieldDesc {
    var mangledTypeName: DWRelativeDirtPointer<CChar>
    var superClass: DWRelativeDirtPointer<CChar>
    var kind: UInt16
    var fieldRecordSize:UInt16
    var numFields: UInt32
    var fields: DWFiledRecordBuffer<DWFieldRecord>
}

struct DWFieldRecord {
    var flags: UInt32
    var mangledTypeName: DWRelativeDirtPointer<CChar>
    var fieldName: DWRelativeDirtPointer<CChar>
}

struct DWFiledRecordBuffer<Element>{
    var element: Element

    mutating func index(of i: Int) -> UnsafeMutablePointer<Element> {
        return withUnsafePointer(to: &self) {
            return UnsafeMutablePointer(mutating: UnsafeRawPointer($0).assumingMemoryBound(to: Element.self).advanced(by: i))
        }
    }
}

func dw_getBitCast(type: Any.Type) -> DWBrigeProtocol.Type {
    let container = DWProtocolMetaData(type: type, witness: 0)
    let bitCast = unsafeBitCast(container, to: DWBrigeProtocol.Type.self)
    return bitCast
}

protocol DWBrigeProtocol {}
extension DWBrigeProtocol {
    static func dw_get(from pointer: UnsafeRawPointer) -> Any {
        return pointer.assumingMemoryBound(to: Self.self).pointee
    }
}

class DWMetaDataManager {
    
    private static var dw_metaDatas : [String : DWModelInfo] = [:]
    
    static func dw_reloadStructInfo(_ type : Any.Type, key : String) -> DWModelInfo {
        var list : [DWJsonPropertyInfo] = []
        let ptr = unsafeBitCast(type, to: UnsafeMutablePointer<DWStructMetaData>.self)
        let numFileds = ptr.pointee.typeDesc.pointee.numFields
        let bufferPtr = UnsafeBufferPointer(start: UnsafeRawPointer(UnsafeRawPointer(ptr).assumingMemoryBound(to: Int.self).advanced(by: numericCast(ptr.pointee.typeDesc.pointee.fieldOffset))).assumingMemoryBound(to: Int32.self), count: Int(ptr.pointee.typeDesc.pointee.numFields))
        for i in 0..<numFileds {
            let fieldRecord = ptr.pointee.typeDesc.pointee.fieldDescriptor.relativeOffset().pointee.fields.index(of: Int(i))
            let fieldName = fieldRecord.pointee.fieldName.relativeOffset()
            let mangledTypeName = fieldRecord.pointee.mangledTypeName.relativeOffset()
            let genericVector = UnsafeRawPointer(ptr).advanced(by: ptr.pointee.typeDesc.pointee.genericOffset * MemoryLayout<UnsafeRawPointer>.size).assumingMemoryBound(to: Any.Type.self)
            let fieldType = dw_swift_getTypeByMangledNameInContext(typeName: mangledTypeName, len: 256, context: UnsafeRawPointer(ptr.pointee.typeDesc), generic: genericVector)
            let realType = unsafeBitCast(fieldType, to: Any.Type.self)
            let fieldOffset = bufferPtr[Int(i)]
            list.append(DWJsonPropertyInfo.init(offset: Int(fieldOffset), jsonKey: (String(cString: fieldName)), type: dw_getBitCast(type: realType)))
        }
        let modelInfo = DWModelInfo()
        modelInfo.propertyList = list
        dw_metaDatas[key] = modelInfo
        return modelInfo
    }
    
    static func dw_reloadClassInfo(_ type : Any.Type, key : String) -> DWModelInfo {
        var list : [DWJsonPropertyInfo] = []
        let ptr = dw_bitCast(type1: type, type2: UnsafeMutablePointer<DWClassMetaData>.self)
        let modelInfo : DWModelInfo = DWModelInfo()
        if let superType = ptr.pointee.superClass {
            let name = String(describing: superType)
            if !(name == "Swift._SwiftObject"
                || name == "NSObject"
                || name == "_TtCs12_SwiftObject") {
                modelInfo.superInfo = dw_reloadModelInfo(superType, displayStyle: .dw_class_style)
            }
        }        
        let offsets = ptr.pointee.typeDesc.pointee.getFieldOffsets(UnsafeRawPointer(ptr).assumingMemoryBound(to: Int8.self))
        for i in 0..<ptr.pointee.typeDesc.pointee.numFields {
            let fieldRecord = ptr.pointee.typeDesc.pointee.fieldDescriptor.relativeOffset().pointee.fields.index(of: Int(i))
            let fieldOffset = offsets[Int(i)]
            let fieldName = fieldRecord.pointee.fieldName.relativeOffset()
            let mangledTypeName = fieldRecord.pointee.mangledTypeName.relativeOffset()
            let genericVector = UnsafeRawPointer(ptr).advanced(by: ptr.pointee.typeDesc.pointee.genericOffset * MemoryLayout<UnsafeRawPointer>.size).assumingMemoryBound(to: Any.Type.self)
            let fieldType = dw_swift_getTypeByMangledNameInContext(typeName: mangledTypeName, len: 256, context: UnsafeRawPointer(ptr.pointee.typeDesc), generic: genericVector)
            let realType = unsafeBitCast(fieldType, to: Any.Type.self)
            list.append(DWJsonPropertyInfo.init(offset: fieldOffset, jsonKey: (String(cString: fieldName)), type: realType))
        }
        modelInfo.propertyList = list
        dw_metaDatas[key] = modelInfo
        return modelInfo
    }
    
    static func dw_reloadModelInfo(_ type : Any.Type, displayStyle : DWJsonDisplayStyle) -> DWModelInfo {
        let key : String = String(describing: type)
        if let _modelInfo = dw_metaDatas[key] {
            return _modelInfo
        }
        var modelInfo : DWModelInfo
        switch displayStyle {
            case .dw_class_style:
                modelInfo = dw_reloadClassInfo(type, key: key)
            case .dw_struct_style:
                modelInfo = dw_reloadStructInfo(type, key: key)
        }        
        if let convertType = type as? DWJsonConvert.Type {
            let propertyMap = convertType.dw_replacePropertyMapper()
            var replaceInfo : DWModelInfo? = modelInfo
            while replaceInfo != nil{
                for i in 0..<replaceInfo!.propertyList.count {
                    let propertyKey = replaceInfo!.propertyList[i].propertyKey
                    if let _propertyMap = propertyMap, _propertyMap.keys.contains(propertyKey), let replaceKey = _propertyMap[propertyKey] {
                        if replaceKey.count > 0 {
                            replaceInfo!.propertyList[i].replaceKey = replaceKey
                            replaceInfo!.propertyList[i].ignoreProperty = false
                        }else {
                            replaceInfo!.propertyList[i].replaceKey = nil
                            replaceInfo!.propertyList[i].ignoreProperty = true
                        }
                    }
                }
                replaceInfo = replaceInfo?.superInfo
            }
        }
        return modelInfo
    }
    
}

enum DWJsonDisplayStyle {
    case dw_class_style
    case dw_struct_style
}
